; window.asm
; Windowing stuff

	.include "common.inc"
	.include "global.inc"
	.include "screen.inc"
	.include "drawfun.inc"
	.include "graphic.inc"
	.include "cursor.inc"
	.include "execfun.inc"
	.include "memory.inc"

	.export win_init
	.export win_create
	.export win_destroy
	.export win_select
	.export win_setpos
	.export win_getpos
	.export win_setdim
	.export win_getdim
	.export win_setfrm
	.export win_getfrm
	.export win_setuser
	.export win_getuser
	.export win_draw
	.export win_settext
	.export win_gettext
	.export win_setfont
	.export win_getfont
	.export win_realize
	.export win_show
	.export win_getstyle
	.export win_setstyle
	.export win_clrstyle
	.export win_begindraw
	.export win_event
	.export win_setdraw
	.export win_setexec
	.export win_settype
	.export win_setstate
	.export win_getstate
	.export win_getparent
	.export win_setparent
	.export win_getindex
	.export win_getfocus
	.export win_setfocus
	.export win_reqfocus
	.export win_isactive
	.export win_isfront

	.export set_capture
	.export rel_capture
	
	.export do_windows
	
	.export frm_create
	.export frm_destroy
	.export frm_getpos
	.export frm_setpos
	.export frm_getwin
	.export frm_realize
	.export frm_show
 	.export frm_setflags
 	.export frm_activate
 	.export frm_center

	.export escalate

	.export dodraw

	.export redraw_screen

	.import sg_initblp
	.import sg_initbl
	.import sg_hide2
 
	.struct VFrame
		block	.byte
		top		.byte
		left	.byte
		height	.byte
		length	.byte	
		flags	.byte
		rtwin	.byte
		rsrvd	.byte
	.endstruct
	
	.struct Window
		index	.byte
		frame	.byte
		style	.byte
		left	.byte
		top		.byte
		width	.byte
		height	.byte
		parent	.byte
		child	.byte
		next	.byte
		font	.byte
		state	.byte
		user	.byte
		drawfn	.word
		eventfn	.word
		text	.word
		focus	.byte
	.endstruct

	W_SIZE = .sizeof (Window)

	WIN_TITLE_HEIGHT = 10

	.segment "DATA"

MAX_FRAMES = 20
MAX_WINDOWS = 96
WCHUNKCNT =	8
WCHUNKSIZE = WCHUNKCNT * W_SIZE 

frames:		.repeat MAX_FRAMES 
				.tag VFrame
			.endrep
			fl_size = *-frames
			f_size	= fl_size/MAX_FRAMES
			f_start = frames - f_size
			.assert f_size = 8, error, "Frame size not equal to 8"

winlo:		.res MAX_WINDOWS
winhi:		.res MAX_WINDOWS
	
	INVALID_HANDLE = $00	

numwins:	.byte 0
freelist:	.byte 0
wroot:		.byte 0
rroot:		.word 0
parent:		.res 1
curWidx:	.res 1
wcapture:	.byte 0

wupdflag:	.byte 0 ; A frame was destroyed (frm_destroy), reset by frm_show.

	.segment "ZEROPAGE"
	
curW:		.res 2

	.segment "DATA"

l_i:		.byte $00
dx:			.byte $00
dy:			.byte $00
by:			.byte $00
bl:			.byte $00
frm:		.byte $00
win:		.byte $00
next:		.byte $00
child:		.byte $00
style_old:	.byte $00
style_new:	.byte $00		

lastmouse:	.tag MouseEvent

	.segment "CODE"

win_init:	; clear curW
			lda #INVALID_HANDLE
			sta freelist
			lda #EVT_MOUSE_ENTER
			sta lastmouse+MouseEvent::type
			lda #0
			sta curW
			sta curW+1
			sta lastmouse+MouseEvent::dx
			sta lastmouse+MouseEvent::dy
			sta lastmouse+MouseEvent::btn
			; clear frames
			tax
@lpclrf:	sta frames, x
			inx
			cpx #fl_size
			bne @lpclrf
			; create root
			jsr newwin
			sta wroot
			ldx curW
			stx rroot
			ldx curW+1
			stx rroot+1
			ldx scr_dim_x
			ldy scr_dim_y
			jsr win_setdim
			lda #WSTYLE_ROOT
			jsr win_setstyle
			lda #<redraw_screen
			ldx #>redraw_screen
			jsr win_setdraw
			lda #<ex_screen
			ldx #>ex_screen
			jsr win_setexec
			rts

growwins:	lda freelist			; check if a free window exists
			bne @done
			lda numwins				; check if max window count reached
			cmp #MAX_WINDOWS
			bcs @done
			lda #WCHUNKSIZE			; alloc chunk
			ldx #0
			jsr malloc
			sta curW
			stx curW+1
			ora curW+1
			beq @done
			ldy #WCHUNKSIZE			; clear chunk
			lda #0
@lpclc:		dey
			sta (curW), y
			bne @lpclc
			ldx	#WCHUNKCNT			; assign chunk to window array
@assignwin:	ldy #Window::next		; (curW)->next = (freelist)
			lda freelist
			sta (curW), y
			ldy numwins				; act index
			clc
			lda curW				; assign to array & adv. curW
			sta winlo, y
			adc #W_SIZE
			sta curW
			lda curW+1
			sta winhi, y
			adc #0
			sta curW+1
			inc numwins				
			lda	numwins				; (freelist) = ++(numwins)
			sta freelist
			dex
			bne @assignwin
@done:		rts	

do_windows:	lda wupdflag
			beq @noupdate
			lda wroot
			jsr selwin
			jsr win_getchild
			beq @noupdate
			jsr selwin
			jsr win_getfrm
			beq @noupdate
			jsr selfrm
			lda f_start+VFrame::flags, x
			and #FFLG_REAL
			bne @noupdate
			lda frm
			jmp frm_activate
@noupdate:	rts

	.segment "DATA"
	
localevt:	.tag WinEvent
pevent:		.res 2

	.segment "CODE"

savevent:	lda reg2		; pevent = reg2
			sta pevent
			lda reg2+1
			sta pevent+1
			rts

restevnt:	lda pevent		; reg2 = pevent
			sta reg2
			lda pevent+1
			sta reg2+1
			rts

dwinevnt:	pha	
			jsr savevent
			pla
			jsr swinevnt
			jsr restevnt
			rts

swinevnt:	stx localevt + WinEvent::type
			sta localevt + WinEvent::win
			pha
			lda #<localevt 
			sta reg2
			lda #>localevt 
			sta reg2+1
			jsr doevent
			pla
			jmp selwin

freewins:	; A .. window index (unchanged)
			pha
			jsr selwin
freewin2:	pla
			pha
			ldx #EVT_WIN_DESTROY
			jsr swinevnt
			ldy #W_SIZE
			lda #0
@lpclw:		; clear win struct
			dey
			sta (curW), y
			bne @lpclw
			ldy #Window::next	; add to freelist
			lda freelist
			sta (curW), y
			pla
			sta freelist
			rts
	
selwin:		; select window in (curW)
			; A ... window index (unchanged), (X .. scratch)
			sta curWidx
			tax
			beq @invalid
			cmp #MAX_WINDOWS+1
			bcs @invalid
			lda winlo-1, x
			sta curW
			lda winhi-1, x
			sta curW+1
			txa
			rts
@invalid:	jsr panic

newwin:		jsr growwins
			lda freelist
			beq @nwfin
			pha
			jsr	selwin
			ldy #Window::next
			lda (curW), y
			sta freelist
			lda #0
			sta (curW), y
			lda #WTYPE_WINDOW
			jsr win_settype
			pla
			ldy #Window::index
			sta (curW), y
@nwfin:		rts

hookrt:		; A .. window index (unchanged), (rroot) .. parent
			ldx rroot
			stx reg2
			ldx rroot+1
			stx reg2+1
			ldx wroot
			stx parent
hookwin:	; A .. window index (unchanged), (parent) .. parent index, (reg2) .. parent
			pha
			jsr selwin
			jsr win_getparent
			beq @noprvrt
			lda curWidx
			jsr unhookwin
@noprvrt:	jsr win_getstyle
			and #WSTYLE_ROOT
			bne @keepfrm
			ldy #Window::frame
			lda (reg2), y
			sta (curW), y
			ldy #Window::font
			lda (reg2), y
			sta (curW), y
@keepfrm:	ldy #Window::child	; this->next = parent->child
			lda (reg2), y
			ldy #Window::next
			sta (curW), y
			lda parent
			jsr win_setparent
			ldy #Window::child	; parent->child = this
			pla
			sta (reg2), y
			rts

unhookwin:	; A .. window index
			sta win
			jsr selwin
			ldy #Window::next
			lda (curW), y
			sta next
			jsr win_getparent
			beq @done
			jsr selwin
			ldy #Window::focus	; check for focus
			lda (curW), y
			cmp win
			bne @notfocus
			lda #0
			sta (curW), y
@notfocus:	jsr win_getchild	; check child(ren) links
@chknxt:	cmp win
			beq @uwstore
			jsr selwin
			jsr win_getnext
			bne @chknxt
			beq @uwfin	
@uwstore:	lda next
			sta (curW), y
@uwfin:		lda win
			jsr selwin
			lda #0
			jsr win_setparent
			ldy #Window::next
			sta (curW), y
@done:		rts

win_create:; A .. parent window, -> A .. window index, (curW)
			cmp #0
			bne	@crapar
			jmp newwin
@crapar:	sta parent
			jsr selwin
			lda curW
			sta reg2
			lda curW+1
			sta reg2+1
			jsr newwin
			cmp #0
			beq @wcfin 
			jsr hookwin
@wcfin:		rts

	.segment "DATA"
	
wdparent:		.byte $00

	.segment "CODE"

win_destroy:; A .. window
			; remove window structure
			pha
			jsr selwin
			jsr win_getparent
			sta wdparent
			lda curWidx
			jsr unhookwin
			lda #0
			jsr win_setfrm
			pla
			tax
			lda	#<prpfrwin
			ldy #>prpfrwin 
			jsr visitpst
			; update mouse cursor (mouse enter event)
			jsr updcursor
			; redraw
			lda wdparent
			beq @noredraw
			cmp wroot
			beq @screen
			jmp win_draw
@screen:	jmp redraw_screen
@noredraw:	rts

updcursor:	; update mouse cursor (mouse enter event)
			lda #<lastmouse
			sta reg2
			lda #>lastmouse
			sta reg2+1
			ldx lastmouse+MouseEvent::mx
			ldy lastmouse+MouseEvent::my
			lda wroot
			jsr win_hit
			jsr doevent
			rts

prpfrwin:	lda curWidx
			pha
			jmp freewin2

win_select:	; A .. window
			jmp selwin

win_setpos:	; (curW), X/Y .. pos
			tya
			ldy #Window::top
			sta (curW), y
			ldy #Window::left
			txa
			sta (curW), y
			rts
			
win_getpos:	; (curW), -> X/Y .. pos
			jsr win_getstyle
			and #WSTYLE_ROOT
			sta l_i
			ldy #Window::left
			lda (curW), y
			tax
			ldy #Window::top
			lda (curW), y
			tay
			lda l_i
			beq @noroot
			txa
			and #7
			tax
			tya
			and #15
			tay
@noroot:	rts
	
win_setdim:	; (curW), X/Y .. dim
			tya
			ldy #Window::height
			sta (curW), y
			ldy #Window::width
			txa
			sta (curW), y
			rts

win_getdim:	; (curW), -> X/Y .. dim
			ldy #Window::width
			lda (curW), y
			tax
			ldy #Window::height
			lda (curW), y
			tay
			rts
	
win_setfrm:	; (curW), A .. frame
			ldy #Window::frame
			sta (curW), y
			rts

win_getfrm:	; (curW), A .. frame
			ldy #Window::frame
			lda (curW), y
			rts

win_setuser:; (curW), A .. user data
			ldy #Window::user
			sta (curW), y
			rts

win_getuser:; (curW), -> A .. user data
			ldy #Window::user
			lda (curW), y
			rts
	
win_settext:; (curW), A/X .. text
			ldy #Window::text
			sta (curW), y
			iny
			txa
			sta (curW), y
			rts
	
win_gettext:; (curW), -> A/X .. text
			ldy #Window::text+1
			lda (curW), y
			tax
			dey
			lda (curW), y
			rts

win_getfont:; (curW), -> A .. font
			ldy #Window::font
			lda (curW), y
			rts

win_setfont:; (curW), A .. font
			ldy #Window::font
			sta (curW), y
			rts

win_getstyle:; (curW), -> A ... style flags
			ldy #Window::style
			lda (curW), y
			rts
	
win_setstyle:; (curW), A ... style flags to set
			sta style_new
			ldy #Window::style
			lda (curW), y
			sta style_old
			ora style_new
			sta (curW), y
			jmp chkstyle

win_clrstyle:; (curW), A ... style flags to clear
			sta style_new
			ldy #Window::style
			lda (curW), y
			sta style_old
			lda style_new
			eor #$FF
			and style_old
			sta style_new
			sta (curW), y
			; fall through

chkstyle:	; A .. new style, Y .. Window::style
			and #WSTYLE_ROOT
			beq @donechk
			lda style_new
			eor style_old
			and #WSTYLE_HASTITLE
			beq @donechk
			; is root window & hastitle changed.
			lda style_new
			and #WSTYLE_HASTITLE
			beq deltitle
			bne addtitle
@donechk:	rts

deltitle:	; search title frame & delete if found
			lda curWidx
			pha
			ldy #Window::child
@fgsrchlp:	lda (curW), y
			beq @donedelt
			sta next
			jsr selwin
			jsr win_getstyle
			bne @srchcnt
			jsr win_getexec
			cmp #<ex_titlebar
			bne @srchcnt	
			cpx #>ex_titlebar
			beq @frfound
@srchcnt:	ldy #Window::next
			bne @fgsrchlp
@frfound:	lda curWidx
			jsr win_destroy
@donedelt:	pla
			jsr selwin
			rts

addtitle:	; add title frame
			lda curWidx
			pha
			jsr win_getdim
			txa
			pha
			lda curWidx
			jsr win_create
			pla
			tax
			ldy #WIN_TITLE_HEIGHT
			jsr win_setdim
			lda #<ex_titlebar
			ldx #>ex_titlebar
			jsr win_setexec
			pla
			jmp selwin

win_draw:	; A ... window index
			pha
			jsr cur_hide
			pla
			tax
			lda #<dodraw
			ldy #>dodraw
			jsr visitpre
			jmp cur_show

win_begindraw:	; (curW)
			jsr win_getfrm
			beq @nofrm
			jsr selfrm
			lda f_start+VFrame::block, x
			jmp g_set_frame
@nofrm:		rts

win_realize:; A .. window index
			pha
			jsr selwin
			jsr win_getfrm
			beq @wrfail
			jsr frm_realize
			pla
			
			jmp hookrt
@wrfail: 	pla
			rts

win_front:	; A .. window index (window to move to front)
			sta win				; win := A
			jsr selwin
			ldy #Window::next	; next := win->next
			lda (curW), y
			sta next
			jsr win_getparent
			beq @fin			; no parent -> done
			jsr selwin
			ldy #Window::child	; check child(ren) links
			lda (curW), y
			cmp win				; already on top?
			beq @fin			; -> done
			sta child 			; child := win->parent->child
			lda win				; window is new child
			sta (curW), y		; win->parent->child := win
			lda child
			ldy #Window::next
@chknxt:	jsr selwin			; find predecessor (prev->next = win)
			lda (curW), y
			beq @err			; window not found -> something wrong!	
			cmp win
			bne @chknxt
@found:		lda next			; prev->next := next
			sta (curW), y
			lda win
			jsr selwin
			lda child			; win->next := child
			ldy #Window::next
			sta (curW), y
@fin:		lda win				; A := win
			rts
@err:		jsr panic

win_show:	; A .. window index
			pha
			jsr cur_hide
			pla
			pha
			jsr win_front
			jsr selwin
			jsr win_getfrm
			beq @wsfail
			jsr frm_show
			pla
			pha
			jsr win_chgfocus
			jsr updcursor
			jsr cur_show
@wsfail: 	pla
			rts
			
win_event:	; A/X .. ptr to event
			sta reg2
			stx reg2+1
			ldy #Event::type
			lda (reg2), y
			and #MOUSE_EVENT
			beq @no_mouse
			; handle mouse event
			ldy #MouseEvent::mx
			lda (reg2), y
			sta lastmouse+MouseEvent::mx
			tax
			ldy #MouseEvent::my
			lda (reg2), y
			sta lastmouse+MouseEvent::my
			tay
			lda wcapture
			beq @nocapture
			jsr selwin
			jmp doevent
@nocapture:	lda wroot
			jsr win_hit
			jsr wchkchng
			jsr wchkfocus
			jsr win_isactive
			bcc mhandleact
			jmp doevent
@no_mouse:	lda (reg2), y
			and #KEY_EVENT
			beq @no_keys
			; handle keyboard event
			lda wroot
@lpfindf:	jsr selwin
			jsr win_getfocus
			beq @ffound
			cmp curWidx
			bne @lpfindf
@ffound:	jmp doevent 
@no_keys:	
@fin:		rts

mhandleact:	; Block mouse events unless it's button_up
			ldy #Event::type
			lda (reg2), y
			cmp #EVT_MOUSE_UP
			bne	@fin
			jsr win_getfrm
			jmp frm_activate		
@fin:		rts

win_setdraw:; A/X .. ptr to event-handler
			ldy #Window::drawfn
			bne w_putptr 

win_setexec:; A/X .. ptr to event-handler
			ldy #Window::eventfn
w_putptr:	sta (curW), y
			iny
			txa
			sta (curW), y
			rts

win_getexec:; (curW) .. window
			; ->  A/X .. ptr to event-handler
			ldy #Window::eventfn+1
			lda (curW), y
			tax
			dey
			lda (curW), y
			rts
			

win_getchild:
			; (curW) .. window
			ldy #Window::child
			lda (curW), y
			rts

win_getnext:
			; (curW) .. window
			ldy #Window::next
			lda (curW), y
			rts

win_getparent:
			; (curW) .. window
			ldy #Window::parent
			lda (curW), y
			rts

win_setparent:
			; (curW) .. window
			ldy #Window::parent
			sta (curW), y
			rts

win_getindex:
			; (curW) .. window
			ldy #Window::index
			lda (curW), y
			rts
			
win_getfocus:
			; (curW) .. window
			ldy #Window::focus
			lda (curW), y
			rts

win_setfocus:
			; (curW) .. window
			ldy #Window::focus
			sta (curW), y
			rts

	.segment "DATA"

newfocus:	.byte $00
oldfocus:	.byte $00
childidx:	.byte $00
parentidx:	.byte $00
	
	.segment "CODE"

win_reqfocus: ; A .. window
			sta newfocus
			jsr selwin
			jsr win_getparent
			beq @fin
			jsr selwin
			jsr win_getfocus
			sta oldfocus
			lda newfocus
			jsr win_setfocus
			lda oldfocus
			beq @fin
			ldx #EVT_WIN_KILLFOCUS
			jsr swinevnt
@fin:		lda newfocus
			jmp selwin

win_chgfocus: ; A .. window index (new focus)
			sta childidx		; select focus child
			jsr selwin
			
			jsr win_getparent
			sta parentidx
			beq @postset
			
			jsr selwin			; select parent
			jsr win_getfocus
			beq @noprev
			
			cmp childidx
			beq @fin
			
			jsr selwin			; select old focus
			ldx #EVT_WIN_KILLFOCUS
			jsr dwinevnt
			
@noprev:	lda parentidx
			jsr selwin			; select parent

			lda childidx
			jsr win_setfocus
			
			jsr selwin			; select focus child
			
@postset:	lda curWidx
			ldx #EVT_WIN_SETFOCUS
			jsr dwinevnt
			
			lda parentidx
			bne win_chgfocus
			
@fin:		rts

win_setstate:
			ldy #Window::state
			sta (curW), y
			rts

win_getstate:
			ldy #Window::state
			lda (curW), y
			rts
	

	.segment "RODATA"

	MAX_TYPES = 3

drawlo:		.byte <drw_window, <drw_button, <drw_edit
drawhi:		.byte >drw_window, >drw_button, >drw_edit
execlo:		.byte <ex_window, <ex_button, <ex_edit
exechi:		.byte >ex_window, >ex_button, >ex_edit	

	.segment "CODE"

win_settype:; A .. window type
			cmp #MAX_TYPES
			bcc @parok
			rts
@parok:		pha
			tay
			lda drawlo, y
			ldx drawhi, y
			jsr win_setdraw
			pla
			tay
			lda execlo, y
			ldx exechi, y
			jmp win_setexec

	.segment "DATA"
	
actwin:		.byte $00
actw:		.word $0000
mevent:		.tag MouseEvent
pwin:		.word $0000
lfwin:		.byte $00
	
	.segment "CODE"

wchkchng:	cmp actwin			; check for a changed mouse event target
			beq @nochng
			pha					; mevent.btn = (reg2).btn
			ldy #MouseEvent::btn
			lda (reg2), y
			sta mevent+MouseEvent::btn
			jsr savevent
			pla
			ldx #<mevent		; reg2 = &mevent
			stx reg2
			ldx #>mevent
			stx reg2+1
			ldx actwin			; actwin == 0?
			cpx #0
			beq @doenter		; yes? -> skip leave event
			pha
			lda curW			; pwin = curW
			sta pwin
			lda curW+1
			sta pwin+1
			lda #EVT_MOUSE_LEAVE
			sta mevent+MouseEvent::type
			lda actw			; curW = actw
			sta curW
			lda actw+1
			sta curW+1
			jsr doevent			; leave event
			lda pwin			; curW = pwin
			sta curW
			lda pwin+1
			sta curW+1
			pla
@doenter:	sta actwin			; actwin = new window index
			lda #EVT_MOUSE_ENTER
			sta mevent+MouseEvent::type
			jsr doevent			; enter event
			lda curW			; actw = curW
			sta actw
			lda curW+1
			sta actw+1
			jsr restevnt
@nochng:	rts

wchkfocus:	ldy #MouseEvent::btn
			lda (reg2), y
			beq @fin
			lda curWidx
			cmp lfwin
			beq @fin
			sta lfwin
			jsr selwin
			jsr win_isactive		; Is new focus target active?
			bcs @goon
			lda wroot
			sta lfwin
@goon:		lda lfwin
			jsr win_chgfocus
			lda lfwin
			jsr selwin
@fin:		rts


	.segment "DATA"
	
px: 		.byte $00		; hit point
py: 		.byte $00
lx:			.byte $00		; point rel. to window origin
ly:			.byte $00
last_hit:	.byte $00		; window index of last match

	.segment "CODE"

win_hit:	; A .. parent, X/Y .. point, -> A,(curW) ... found top-most window (or 0)
			sta win
			stx px
			sty py
			jsr selwin
			lda px
			sec
			ldy #Window::left
			sbc (curW), y
			bcc @no_hit
			sta lx
			ldy #Window::width
			cmp (curW), y
			bcs @no_hit
			lda py
			sec
			ldy #Window::top
			sbc (curW), y
			bcc @no_hit
			sta ly
			ldy #Window::height
			cmp (curW), y
			bcs @no_hit
			; found
			lda win
			sta last_hit
			; calc relative position for root windows
			; assumptions:
			;	root window = screen | frame root
			;	active frames do not overlap ??
			;	windows of inactive frames are not children of screen
			; check children
			jsr win_getstyle
			and #WSTYLE_ROOT
			beq @notrt
			lda lx
			sta px
			lda ly
			sta py
@notrt:		lda #0
			sta curpos
			jsr enochldrn	; x/curpos .. nextpos -> curW invalid!
			lda #0
			sta l_i
@lpchldrn:	ldx l_i
			cpx curpos
			beq @no_hit
			inc l_i
			lda last_hit
			pha
			lda scratchbuf1, x
			ldx px
			ldy py
			jsr win_hit
			pla
			cmp last_hit
			beq @lpchldrn
@no_hit:	lda last_hit
			jsr selwin
			lda last_hit
			rts

	.segment "JMPTABLE"
	
drawfn:		.res 2
eventfn:	.res 2

	.segment "CODE"

dodraw:		jsr win_isactive
			bcc @nulfn
			ldy #Window::drawfn
			lda (curW), y
			sta drawfn
			iny
			lda (curW), y
			sta drawfn+1
			ora drawfn
			beq @nulfn
			jmp (drawfn)
@nulfn:		rts

doevent:	ldy #Window::eventfn
			lda (curW), y
			sta eventfn
			iny
			lda (curW), y
			sta eventfn+1
			ora eventfn
			beq @nulfn
			jmp (eventfn)
@nulfn:		rts

drawhid:	; (curW) .. window to draw
			jsr win_getfrm
			jsr selfrm
			txa
			pha
			lda f_start+VFrame::top, x
			tay
			lda f_start+VFrame::left, x
			tax
			jsr sg_initblp
			pla
			tax
			lda f_start+VFrame::length, x
			tay
			lda f_start+VFrame::height, x
			tax			
			jsr sg_initbl
			jmp sg_hide2

;
;	direct all events to current window
;
set_capture:
			jsr win_getindex
			sta wcapture
			rts

;
;	undo the effects of set_capture
;
rel_capture:
			lda #0
			sta wcapture
			rts

selfrm:		sta frm
			cmp #0
			beq @fsfail
			asl
			asl
			asl
			tax
			rts
@fsfail:	pla
			pla
			lda #0
			rts

frm_getpos: ; A .. frame index -> X/Y pos
			jsr selfrm
			lda f_start + VFrame::rtwin, x
			jsr selwin
			ldy #Window::left
			lda (curW), y
			tax
			ldy #Window::top
			lda (curW), y
			tay
			lda frm
			rts

frm_setpos: ; A .. frame index, X/Y pos
			pha
			txa
			and #$F8
			sta dx
			tya
			and #$F0
			sta dy
			pla
			jsr selfrm
isetpos:	lda dx
			lsr
			lsr
			lsr
			sta f_start + VFrame::left, x
			lda dy
			lsr
			lsr
			lsr
			lsr
			sta f_start + VFrame::top, x
			lda f_start + VFrame::rtwin, x
			beq @fsfin
			ldx dx
			ldy dy
			jsr win_setpos
@fsfin:		lda frm
			rts

frm_center: ; A .. frame index (unchanged)
			pha				; save frame index
			jsr selfrm
			; center vertically
			sec
			lda num_rows
			sbc f_start + VFrame::height, x
			and #$FE
			asl
			asl
			asl
			sta dy
			; center horizontally
			ldy #0
			lda f_start + VFrame::length, x
@divlp:		iny
			sec
			sbc f_start + VFrame::height, x
			bne @divlp
			sty dx
			sec
			lda num_cols
			sbc dx
			and #$FE
			asl
			asl
			sta dx
			; set position
			jsr isetpos
			pla				; restore frame index
			rts
			
frm_getwin: ; A .. frame index, -> A .. window index
			jsr selfrm
			lda f_start +  VFrame::rtwin, x
			rts

frm_create:	; X ... width, Y ..height, -> A .. frame index
			txa
			clc
			adc #7
			lsr
			lsr
			lsr
			sta l_i
			asl
			asl
			asl
			sta dx
			tya
			clc
			adc #15
			lsr
			lsr
			lsr
			lsr
			beq @fcfail	; no height
			tay
			asl
			asl
			asl
			asl
			sta dy
			sty by
			clc
			lda #0
@mull:		adc l_i
			bcs @fcfail	; overflow
			dey
			bne @mull
			; A = length
			sta bl
			jsr newfrm
			jmp initfrm
@fcfail:	lda #0	
			rts

	.segment "DATA"
	
tmpflags:	.byte $00	

	.segment "CODE"

frm_realize:
			; A .. frame index
			pha
			jsr cur_hide
			pla
			jsr selfrm
			lda f_start+VFrame::flags, x
			sta tmpflags
			and #FFLG_REAL
			bne @frdone
			lda #FFLG_PERM
			ora tmpflags
			and #~FFLG_TEMP
			sta f_start+VFrame::flags, x
			lda f_start+VFrame::height, x
			tay
			txa
			pha
			lda f_start+VFrame::length, x
@try_alloc:	jsr sg_alloc					; (A = size, Y = height)
			cpy #$FF
			bne @alloc_ok
			jsr frm_cleanup					; -> carry set = block(s) released
			bcc @frdone						; no success
			pla
			tax
			pha
			lda f_start+VFrame::height, x
			tay
			lda f_start+VFrame::length, x
			bne @try_alloc
			jsr panic
@alloc_ok:	pla
			tax
			tya
			sta f_start+VFrame::block, x
			lda tmpflags
			ora #FFLG_REAL
			sta f_start+VFrame::flags, x
@frdone:	jmp cur_show

frm_show:	; A .. frame index
			jsr selfrm
			lda #0
			sta wupdflag
			lda f_start+VFrame::flags, x
			and #FFLG_REAL
			beq fsdone
frshowint:	lda f_start+VFrame::block, x
			pha
			lda f_start+VFrame::top, x
			tay
			lda f_start+VFrame::left, x
			tax
			pla
			jsr sg_show
fsdone:		rts

frm_setflags:
			; A .. frame index, Y .. flags to set
			jsr selfrm
			tya
			ora f_start+VFrame::flags, x
			sta f_start+VFrame::flags, x
			tay
			lda frm
			rts

frm_getflags:
			; A .. frame index, -> Y .. flags
			jsr selfrm
			lda f_start+VFrame::flags, x
			tay
			lda frm
			rts

initsrch:	lda #0
			tay
			tax
			clc
			rts

srchnxt:	txa
			clc
			adc #f_size
			iny
			cpy #MAX_FRAMES
			rts

frm_cleanup:	; try to release frames (first FLG_TEMP, then without FLG_PERM) -> C=1 .. success
			pha
			txa
			pha
			tya
			pha
			jsr initsrch
@srchtmp:	jsr srchnxt
			bcs @fcnotmp
			tax
			lda f_start+VFrame::flags, x
			and #FFLG_USED|FFLG_REAL|FFLG_TEMP|FFLG_PERM
			cmp #FFLG_USED|FFLG_REAL|FFLG_TEMP
			bne @srchtmp
			; found a temporary frame -> destroy it
			tya
			jsr frm_destroy
			jmp @fcok
@fcnotmp:	jsr initsrch
@srchnorm:	jsr srchnxt
			bcs @fcfail
			tax
			lda f_start+VFrame::flags, x
			and #FFLG_USED|FFLG_REAL|FFLG_PERM
			cmp #FFLG_USED|FFLG_REAL
			bne @srchnorm
			; found a normal frame -> deactivate
			tya
			jsr frm_deactivate
@fcok:		sec
			.byte $90	; bcc
@fcfail:	clc
			pla
			tay
			pla
			tax
			pla
			rts

frm_deactivate:
			; A .. frame index
			jsr selfrm
			lda f_start+VFrame::flags, x
			and #FFLG_REAL
			beq @fdfin
			lda f_start+VFrame::flags, x
			and #~FFLG_REAL
			sta f_start+VFrame::flags, x
			lda f_start+VFrame::block, x	; free screen block
			jsr sg_free
			jmp redraw_screen
@fdfin:		rts

	.segment "DATA"
	
awin:		.byte $00

	.segment "CODE"

frm_activate:
			; A .. frame index
			jsr selfrm
			lda f_start+VFrame::rtwin, x
			sta awin
			lda f_start+VFrame::flags, x
			and #FFLG_REAL
			bne @no_realize
			lda awin
			jsr win_realize
@no_realize:
			lda awin
			jsr win_draw
			lda awin
			jmp win_show

newfrm:		; -> Y ... new frame, X .. offset
			jsr initsrch
@nfsrch:	jsr srchnxt
			bcs @nffail
			tax
			lda f_start+VFrame::flags, x
			and #FFLG_USED
			bne @nfsrch
			rts
@nffail:	ldy #0
			rts
	
initfrm:	; Y ... new frame, X .. offset, -> A frame index
			lda #FFLG_USED
			sta f_start+VFrame::flags, x
			lda #0
			sta f_start+VFrame::block, x
			sta f_start+VFrame::top, x
			sta f_start+VFrame::left, x
			sta f_start+VFrame::rsrvd, x
			lda by
			sta f_start+VFrame::height, x
			lda bl
			sta f_start+VFrame::length, x
			tya
			pha
			txa
			pha
			lda #0
			jsr win_create
			pha
			lda #WSTYLE_ROOT
			jsr win_setstyle
			pla
			tay
			pla
			tax
			tya
			sta	f_start+VFrame::rtwin, x 
			ldx dx
			ldy dy
			jsr win_setdim
			pla
			pha
			jsr win_setfrm
			pla
			rts

frm_destroy:; A .. frame index
			jsr	selfrm
			stx frm
			lda #1
			sta wupdflag
			lda f_start+VFrame::flags, x
			and #FFLG_REAL
			beq @skpblk
			lda f_start+VFrame::block, x	; free screen block
			jsr sg_free
@skpblk:	ldx frm							; free frame
			lda #0
			sta f_start+VFrame::flags, x
			lda f_start+VFrame::rtwin, x	; destroy window
			beq @skpwin
			pha
			jsr cur_hide
			pla 
			jsr win_destroy
			jmp cur_show
@skpwin:	rts

;
; Event escalation
;

escalate:	; send current event (reg2) to parent of window in (curW)
			jsr win_getparent
			bne @doesc
			rts
@doesc:		jsr selwin
			jmp doevent

;
; Tree-Walking for Window structures
;
	.segment "JMPTABLE"

preorder:	.res 2
postorder:	.res 2

	.segment "DATA"

curpos:		.byte $00
tmpsav:		.byte $00

	.segment "CODE"

emptycall:	rts

callpre:	jmp (preorder)

callpost:	jmp (postorder)

win_isactive: ; (curW) ... window, Carry = active, -> X unchanged!!
			ldy #Window::style	; window active?
			lda (curW), y
			and #WSTYLE_ROOT	; root window?
			beq @isactive
			jsr win_getfrm		; is frame realized?
			beq @isactive
			stx tmpsav 
			jsr frm_getflags
			ldx tmpsav
			tya
			and #FFLG_REAL
			bne @isactive		
			clc
			rts
@isactive:	sec					; window active
			rts

win_isfront: ; A ... window, Carry = is in front
			sta child
			jsr win_getindex
			sta win
			lda child
			jsr selwin
			jsr win_getparent
			beq @ishidden
			jsr selwin
			jsr win_getchild
			cmp child
			beq @isfront
@ishidden:	lda win
			jsr selwin
			clc
			rts
@isfront:	lda win
			jsr selwin
			sec					; window in front
			rts

enochldrn:	; Enumerate children of a window, no children if inactive
			; (curW) ... parent, curpos .. pos in scratchbuf1
			; -> x ... last pos, l_i .. count
			lda #0
			sta l_i
			jsr win_isactive
			bcc @ccdone
			ldy #Window::child	; collect children
@ldnext:	lda (curW), y		 
			beq @ccdone
			ldx curpos
			inc curpos
			sta scratchbuf1, x	; push window index
			jsr selwin
			inc l_i
			ldy #Window::next
			bne @ldnext
@ccdone:				; x = last pos
			rts

wlkwins:	; A .. window index, uses scratchbuf1 as window stack
			ldx curpos			; push window index
			inc curpos
			sta scratchbuf1, x
			jsr selwin			; select window struct & pre-oder call			
			jsr callpre
			jsr enochldrn		; x = last pos
			lda l_i
			cmp #0
			beq @wwfin
			ldx curpos
@wlknxt:	stx curpos			; save buf ptr
			dex
			tay
			lda scratchbuf1, x	; read next window index
			pha
			dey					; save l_i-1 (remaining windows)
			tya
			sta scratchbuf1, x
			pla
			jsr wlkwins			; walk recursively
			ldx curpos 			; retrieve remaining windows
			dex
			lda scratchbuf1, x
			bne @wlknxt
			stx curpos
@wwfin:		dec curpos			; pop window index
			ldx curpos
			lda scratchbuf1, x
			jsr selwin			; select window struct & post-order call
			jsr callpost		
			rts

visitpre:	; A/Y visit fn (pre-order), X .. window index
			sta preorder
			sty preorder+1
			lda #<emptycall
			sta postorder
			lda #>emptycall
			sta postorder+1
			txa
			jmp wlkwins

visitpst:	; A/Y visit fn (pre-order), X .. window index
			sta postorder
			sty postorder+1
			lda #<emptycall
			sta preorder
			lda #>emptycall
			sta preorder+1
			txa
			jmp wlkwins

redraw_screen:
			; Rebuilds video ram contents
			; no regs saved, current window changed.
			lda #0
			sta wincnt
			lda wroot
			jsr selwin
			jsr win_getchild
			beq @gotall
@lpchldrn:	ldx wincnt
			inc wincnt
			sta winlist, x
			jsr selwin
			jsr win_getnext
			bne @lpchldrn
@gotall:	jsr sg_bufbeg
			ldx wincnt
			beq @fin		
@drawwins:	dex 
			stx wincnt
			lda winlist, x
			jsr selwin
			jsr win_isactive
			bcs @active
			jsr drawhid
			jmp @next
@active:	jsr win_getfrm
			beq @next
			jsr selfrm
			jsr frshowint
@next:		ldx wincnt
			bne @drawwins
@fin:		jmp sg_bufend
			
	.segment "DATA"

wincnt:		.byte 0
winlist:	.res MAX_WINDOWS
